//
//  MainScriptPanelVC.swift
//  Hermes
//
//  Created by FH on 2021/5/19.
//  Copyright © 2021 Agora. All rights reserved.
//

import Foundation

enum ScriptInfoBelong : String {
    case none = ""
    case action
    case script
    case subscribe
}
enum ScriptInfoType : String {
    case none
    case directive
    case `operator`
    case instruction
}

class ScriptInfo {
    var pos = 0
    var belong: ScriptInfoBelong = .none
    var type: ScriptInfoType = .none
    var bundleName = ""
    var invokeInfo = ""
    var remark = ""
    
    static func loadFrom(_ instr: ScriptInstruction, _ bundleName: String) -> [ScriptInfo] {
        var result = [ScriptInfo]()
        let instrInfo = ScriptInfo()
        instrInfo.pos = instr.pos
        instrInfo.bundleName = bundleName
        instrInfo.type = .instruction
        if bundleName == "main" {
            instrInfo.belong = .script
        } else if bundleName == "actions" || bundleName == "settings" {
            instrInfo.belong = .action
        } else {
            instrInfo.belong = .subscribe
            if let (selector, blocker, _) = instr.subscribeCallback {
                instrInfo.invokeInfo = "subscribe: \(blocker ?? selector)"
            } else {
                instrInfo.invokeInfo = "followed instr "
            }
        }
        if instr.associatedActions.count > 0 {
            instrInfo.invokeInfo = "name: \(instr.associatedActions.joined(separator: ", "))"
        }
        if instr.canInvoking {
            if instr.isBreakPoint {
                instrInfo.remark += ".breakPoint "
            }
            if instr.waitCallback != nil {
                instrInfo.remark += ".waitCallback "
            }
            if instr.delayTime > 0 || (instr.runInMainThread != nil && instr.runInMainThread! == false) {
                instrInfo.remark += "asyncInvoke "
            }
            if instr.isDefault {
                instrInfo.remark += "defaultInstr "
            } else if instr.isReplaced {
                instrInfo.remark += "replacedInstr "
            } else if instr.belongSettings {
                instrInfo.remark += "belongSettings "
            }
        } else {
            instrInfo.remark = "not predicate"
        }
        result.append(instrInfo)

        for op in instr.operators {
            let opInfo = ScriptInfo()
            opInfo.pos = instr.pos
            opInfo.type = .operator
            if let selector = op.selector {
                opInfo.invokeInfo = "call: \(selector)"
            } else if op.forInvokeDirectives {
                opInfo.invokeInfo = "onlyDirectives"
            } else if op.forCreateInstance {
                let targetClass = op.targetClass != nil ? "(\(op.targetClass!))" : ""
                opInfo.invokeInfo = "newInstance\(targetClass)"
            } else if op.forSetProperties {
                opInfo.invokeInfo = "setProperties"
            }
            if let canInvoke = instr.checkedPredicateContext[op], !canInvoke {
                opInfo.remark = "not predicate "
            }
            if op.waitCallAPIDirective != nil {
                opInfo.remark += ".waitCallAPI "
            }
            result.append(opInfo)
        }
        
        return result
    }
    
    func toDataItem() -> [String : String] {
        return [
            "pos": "\(pos)",
            "belong": belong.rawValue,
            "type": type.rawValue,
            "bundle": bundleName,
            "invokeInfo": invokeInfo,
            "remark": remark
        ]
    }
}

extension Array where Element == ScriptInfo {
    func toDataItems() -> [[String : String]] {
        var items = [[String : String]]()
        for info in self {
            items.append(info.toDataItem())
        }
        return items
    }
}

//////////////////////////////////////////////

class MainScriptPanelVC : FLViewController {
    private let mutex = Mutex()
    private let appContext = HermesContext()
    private let headerView = NSView()
    private let bodyView = NSView()
    // header
    private let inputFrameworksDir = FLInputField()
    private let btnChooseAPIsPath = NSButton()
    private let btnScanAPIs = NSButton()
    private let inputParamsDir = FLInputField()
    private let btnChooseParamsPath = NSButton()
    private let btnScanParams = NSButton()
    private let inputCustomScriptsDir = FLInputField()
    private let btnScanScripts = NSButton();
    private let btnTabScript = NSButton()
    private let btnTabLogger = NSButton()
    // body
    private let selectScripts = NSPopUpButton()
    private let btnEditScript = NSButton()
    private let btnAddScript = NSButton()
    private let btnCustomScript = NSButton()
    private let btnSwitchScript = NSButton()
    private let selectInvokeMode = NSPopUpButton()
    private let lblRunMode = FLLabel()
    private let btnInvokeScript = NSButton()
    private let btnNextStep = NSButton()
    private let btnChangeToManual = NSButton()
    private var table: FLTableView<String, VoidRowCell>! = nil
    // inner states
    private var notRender = true
    private var dataSource = [ScriptInfo]()
    // rac trigger tabContainer
    private var rac_tabLoggerSubject: IOSubject<NSButton>!
    var rac_tabLogger: IOMonad<NSButton> {
        if rac_tabLoggerSubject == nil {
            self.rac_tabLoggerSubject = IOSubject<NSButton>()
            self.btnTabLogger.rac_eventTouch.subscribeNext {[weak self] _ in
                if let self = self {
                    self.rac_tabLoggerSubject.sendNext(self.btnTabLogger)
                }
            }
        }
        return self.rac_tabLoggerSubject.signal
    }

    // REMARK: bind data
    
    private func dataBinding(_ needsLayout: Bool) {
        if self.selectScripts.itemTitles.count == 0 {
            self.scanAllScripts(nil)
            self.appContext.$loadAPIsPath.signal().subscribeNext {[weak self] path in
                self?.inputFrameworksDir.flText = path
            }
            self.appContext.$loadParamsPath.signal().subscribeNext {[weak self] path in
                self?.inputParamsDir.flText = path
            }
            self.appContext.preferences.$customScriptsLocation.signal().subscribeNext {[weak self] path in
                if let path = path {
                    self?.inputCustomScriptsDir.flText = path
                }
            }
            self.selectInvokeMode.addItems(withTitles: ScriptsInvokeMode.allCaseDescriptions)
            self.appContext.mainStates.$invokeMode.bindTo(self.selectInvokeMode)
            RACAssign(self.appContext.mainStates).invokeMode = self.selectInvokeMode.rac_eventTouch.map { $0.selectedComplexEnumIndex }
            self.appContext.scriptsInvoker.$runningMode.bindTo(lblRunMode) {[weak self] (lbl, mode) in
                if let self = self {
                    lbl.flText = mode.rawValue
                    self.selectScripts.isEnabled = mode == .notStart
                    self.selectInvokeMode.isEnabled = mode == .notStart
                    self.btnChooseAPIsPath.isEnabled = mode == .notStart
                    self.btnChooseParamsPath.isEnabled = mode == .notStart
                    self.btnScanAPIs.isEnabled = mode == .notStart
                    self.btnScanParams.isEnabled = mode == .notStart
                    self.btnScanScripts.isEnabled = mode == .notStart
                    self.btnEditScript.isEnabled = mode == .notStart
                    self.btnAddScript.isEnabled = mode == .notStart
                    self.btnCustomScript.isEnabled = mode == .notStart
                    self.btnSwitchScript.isEnabled = mode == .notStart
                    self.btnNextStep.isEnabled = (mode == .pause && self.appContext.mainStates.invokeMode == .semiAutomatic)
                    self.btnChangeToManual.isEnabled = (mode == .pause && self.appContext.mainStates.invokeMode == .semiAutomatic)
                    self.btnInvokeScript.flTitle = (mode == .notStart) ? "Run" : "Stop"
                    self.btnInvokeScript.flTitleColor = (mode == .notStart) ? .white : .orange
                    self.btnInvokeScript.flBackgroundColor = (mode == .notStart) ? .orange : .white
                    self.btnInvokeScript.font = (mode == .notStart) ? NSFont.systemFont(ofSize: 13) : NSFont.boldSystemFont(ofSize: 15)
                }
            }
            // header buttons
            let chooseAPIsOrParamsFileFn: Action2<Bool, NSButton> = {[weak self] (forParams, btn) in
                if let self = self {
                    var chooseDir = ""
                    let path = forParams ? self.inputFrameworksDir.flText : self.inputFrameworksDir.flText
                    if FileManager.default.fileExists(atPath: path) {
                        let url = URL(fileURLWithPath: path)
                        if url.lastPathComponent.hasSuffix(".json") {
                            chooseDir = path.getSubstring(to: path.count - url.lastPathComponent.count - 1)
                        }
                    } else {
                        let userName = NSUserName()
                        chooseDir = "/Users/\(userName)/Desktop"
                    }
                    let dialog = NSOpenPanel()
                    dialog.directoryURL = URL(fileURLWithPath: chooseDir, isDirectory: true)
                    dialog.canChooseFiles = true
                    dialog.canChooseDirectories = false
                    dialog.allowedFileTypes = ["json"]
                    dialog.beginSheetModal(for: self.view.window!) {[weak self, weak dialog] res in
                        if res.rawValue == NSApplication.ModalResponse.OK.rawValue, let url = dialog?.url {
                            if let self = self,
                               self.appContext.checkAPIsNParamsPaths(url.path, forParams: forParams) {
                                let tip = forParams ? "reload private params success!" : "reload apis engine success!"
                                FLShowCloseAlert("choose \(url.lastPathComponent) and \(tip)")
                            } else {
                                FLShowCloseAlert("selected \(url.lastPathComponent) is not match current sdk version: \(self!.appContext.sdkVersion)")
                            }
                            btn.flTitle = btn.flTitle.switchButtonTitle()
                        }
                    }
                }
            }
            self.btnChooseAPIsPath.rac_eventTouch.subscribeNext { btn in
                chooseAPIsOrParamsFileFn(false, btn)
            }
            self.btnChooseParamsPath.rac_eventTouch.subscribeNext { btn in
                chooseAPIsOrParamsFileFn(true, btn)
            }
            self.btnScanAPIs.rac_eventTouch.subscribeNext {[weak self] btn in
                if let self = self {
                    FLShowCloseAlert("该功能即将开放使用")
                    btn.flTitle = btn.flTitle.switchButtonTitle()
                }
            }
            self.btnScanParams.rac_eventTouch.subscribeNext {[weak self] btn in
                if let self = self {
                    FLShowCloseAlert("该功能即将开放使用")
                    btn.flTitle = btn.flTitle.switchButtonTitle()
                }
            }
            self.btnScanScripts.rac_eventTouch.subscribeNext {[weak self] btn in
                if let self = self {
                    var chooseDir = self.inputCustomScriptsDir.flText
                    if !FileManager.default.fileExists(atPath: chooseDir) {
                        let userName = NSUserName()
                        chooseDir = "/Users/\(userName)/Desktop"
                    }
                    let dialog = NSOpenPanel()
                    dialog.directoryURL = URL(fileURLWithPath: chooseDir, isDirectory: true)
                    dialog.canChooseFiles = false
                    dialog.canChooseDirectories = true
                    dialog.beginSheetModal(for: self.view.window!) {[weak self, weak dialog] res in
                        if res.rawValue == NSApplication.ModalResponse.OK.rawValue, let url = dialog?.url {
                            if let self = self {
                                self.scanAllScripts(url.path)
                                FLShowCloseAlert("load custom scripts success.")
                                btn.flTitle = btn.flTitle.switchButtonTitle()
                            } else {
                                FLShowCloseAlert("selected \(url.lastPathComponent) is not match current sdk version: \(self!.appContext.sdkVersion)")
                            }
                            btn.flTitle = btn.flTitle.switchButtonTitle()
                        }
                    }
                }
            }
            // body buttons
            self.btnEditScript.rac_eventTouch.subscribeNext {[weak self] btn in
                if let self = self {
                    FLShowCloseAlert("该功能即将开放使用")
                    btn.flTitle = btn.flTitle.switchButtonTitle()
                }
            }
            self.btnAddScript.rac_eventTouch.subscribeNext {[weak self] btn in
                if let self = self {
                    FLShowCloseAlert("该功能即将开放使用")
                    btn.flTitle = btn.flTitle.switchButtonTitle()
                }
            }
            self.btnCustomScript.rac_eventTouch.subscribeNext {[weak self] btn in
                if let self = self {
                    FLShowCloseAlert("该功能即将开放使用")
                    btn.flTitle = btn.flTitle.switchButtonTitle()
                }
            }
            self.btnSwitchScript.rac_eventTouch.subscribeNext {[weak self] _ in
                if let self = self {
                    let index = self.selectScripts.indexOfSelectedItem
                    let scriptName = self.selectScripts.itemTitles[index]
                    if self.appContext.preferences.currScript != scriptName {
                        self.appContext.loadScriptBundles(scriptName)
                    }
                }
            }
            // run script
            self.btnInvokeScript.rac_eventTouch.subscribeNext {[weak self] _ in
                if let self = self {
                    if self.appContext.scriptsInvoker.runningMode == .notStart && self.dataSource.count > 0 {
                        self.dataSource.removeAll()
                        self.table.cleanData(needReload: true)
                    }
                    self.appContext.scriptsInvoker.invoke(mode: self.appContext.mainStates.invokeMode)
                }
            }
            self.btnNextStep.rac_eventTouch.subscribeNext {[weak self] _ in
                if let self = self {

                }
            }
            self.startMonitorRender()
            self.appContext.scriptsInvoker.addProcessObserver {[weak self] (instr, bundleName) in
                if let self = self, self.appContext.scriptsInvoker.runningMode != .notStart {
                    let data = ScriptInfo.loadFrom(instr, bundleName)
                    let succ = self.mutex.tryLock()
                    self.dataSource.append(contentsOf: data)
                    if !self.notRender {
                        self.table.appendData(columnDatas: data.toDataItems())
                    }
                    if succ {
                        self.mutex.unlock()
                    }
                }
            }
        }
    }
    
    private func scanAllScripts(_ customScriptsPath: String?) {
        self.selectScripts.removeAllItems()
        let items = self.appContext.scanScriptList(customScriptsPath: customScriptsPath)
        self.selectScripts.addItems(withTitles: items)
        let currScript = self.appContext.preferences.currScript
        if currScript != ScriptBundleType.default.rawValue,
           let index = items.firstIndex(where: { $0.hasSuffix("-\(currScript)")}) {
            self.selectScripts.selectItem(at: index)
        }
        self.appContext.loadScriptBundles(nil)
    }
    
    // REMARK: layout UI
    
    override func didRemovedFromParent() {
        if !self.notRender {
            self.notRender = true
            self.table = nil
        }
    }
    
    override func didLayoutUI() {
        if self.notRender && self.headerView.subviews.count > 0 {
            self.notRender = false
            if self.dataSource.count > 0 {
                self.table.appendData(columnDatas: self.dataSource.toDataItems())
            }
        }
    }
    
    override func layoutUI(needsLayout: Bool, args: [String : Any?]? = nil) {
        self.dataBinding(needsLayout)
        self.layoutHeaderView(needsLayout)
        self.layoutBodyView(needsLayout)
    }
    
    private func layoutHeaderView(_ needsLayout: Bool) {
        self.headerView.bind(to: yoga, self.view) { it in
            it.direction = .row
            it.height = 30
            it.alignItems = .center
        }
        self.headerView.flBackgroundColor = .lightGray
        
        self.inputFrameworksDir.bind(to: yoga, self.headerView) { it in
            it.width = 170
            it.height = 26
            it.marginLeft = 4
        }
        self.btnChooseAPIsPath.bind(to: yoga, self.headerView) { it in
            it.width = 110
            it.height = 20
            it.marginLeft = 2
        }
        self.btnChooseAPIsPath.flTitle = "Choose APIs Dir"
        self.btnChooseAPIsPath.flTitleColor = .white
        self.btnChooseAPIsPath.flBackgroundColor = .gray
        self.btnChooseAPIsPath.font = NSFont.boldSystemFont(ofSize: 12)
        self.btnChooseAPIsPath.flCornerRadius = 4
        self.btnScanAPIs.bind(to: yoga, self.headerView) { it in
            it.width = 120
            it.height = 20
            it.marginLeft = 4
        }
        self.btnScanAPIs.flTitle = "Generate API"
        self.btnScanAPIs.flTitleColor = .white
        self.btnScanAPIs.flBackgroundColor = .gray
        self.btnScanAPIs.font = NSFont.boldSystemFont(ofSize: 12)
        self.btnScanAPIs.flCornerRadius = 4
        
        self.inputParamsDir.bind(to: yoga, self.headerView) { it in
            it.width = 170
            it.height = 26
            it.marginLeft = 12
        }
        self.btnChooseParamsPath.bind(to: yoga, self.headerView) { it in
            it.width = 120
            it.height = 20
            it.marginLeft = 4
        }
        self.btnChooseParamsPath.flTitle = "Choose Params Dir"
        self.btnChooseParamsPath.flTitleColor = .white
        self.btnChooseParamsPath.flBackgroundColor = .gray
        self.btnChooseParamsPath.font = NSFont.boldSystemFont(ofSize: 12)
        self.btnChooseParamsPath.flCornerRadius = 2
        self.btnScanParams.bind(to: yoga, self.headerView) { it in
            it.width = 120
            it.height = 20
            it.marginLeft = 4
        }
        self.btnScanParams.flTitle = "Generate Params"
        self.btnScanParams.flTitleColor = .white
        self.btnScanParams.flBackgroundColor = .gray
        self.btnScanParams.font = NSFont.boldSystemFont(ofSize: 12)
        self.btnScanParams.flCornerRadius = 4
        
        self.inputCustomScriptsDir.bind(to: yoga, self.headerView) { it in
            it.width = 170
            it.height = 26
            it.marginLeft = 12
        }
        self.btnScanScripts.bind(to: yoga, self.headerView) { it in
            it.width = 120
            it.height = 20
            it.marginLeft = 4
        }
        self.btnScanScripts.flTitle = "Choose Scripts Dir"
        self.btnScanScripts.flTitleColor = .white
        self.btnScanScripts.flBackgroundColor = .gray
        self.btnScanScripts.font = NSFont.boldSystemFont(ofSize: 12)
        self.btnScanScripts.flCornerRadius = 4
        // right span
        let rightHeaderSpan = NSView()
        rightHeaderSpan.bind(to: yoga, self.headerView) { it in
            it.position = .absolute
            it.direction = .row
            it.alignItems = .center
            it.right = 4
            it.height = 30
        }
        
        self.btnTabScript.bind(to: yoga, rightHeaderSpan) { it in
            it.width = 80
            it.height = 26
        }
        self.btnTabScript.flTitle = "Script Info"
        self.btnTabScript.flTitleColor = .white
        self.btnTabScript.flBackgroundColor = .darkGray
        self.btnTabScript.font = NSFont.boldSystemFont(ofSize: 12)
        self.btnTabScript.flCornerRadius = 4
        
        self.btnTabLogger.bind(to: yoga, rightHeaderSpan) { it in
            it.width = 80
            it.height = 26
        }
        self.btnTabLogger.flTitle = "Logger Info"
        self.btnTabLogger.flTitleColor = .white
        self.btnTabLogger.flBackgroundColor = .gray
        self.btnTabLogger.font = NSFont.boldSystemFont(ofSize: 12)
        self.btnTabLogger.flCornerRadius = 4
    }
    
    private func layoutBodyView(_ needsLayout: Bool) {
        self.bodyView.bind(to: yoga, self.view) { it in
            it.direction = .row
            it.flexGrow = 1
            it.alignItems = .center
        }
        // left span
        let leftBodySpan = NSView()
        leftBodySpan.bind(to: yoga, self.bodyView) { it in
            it.width = 180
            it.marginLeft = 4
            it.marginRight = 24
        }
        self.selectScripts.bind(to: yoga, leftBodySpan) { it in
            it.width = 180
            it.height = 30
        }
        // line1 of leftBody
        let line1OfLeftBodySpan = NSView()
        line1OfLeftBodySpan.bind(to: yoga, leftBodySpan) { it in
            it.direction = .row
            it.height = 30
            it.alignItems = .center
        }
        self.btnEditScript.bind(to: yoga, line1OfLeftBodySpan) { it in
            it.height = 26
            it.width = 80
            it.marginLeft = 4
        }
        self.btnEditScript.flTitle = "Edit Script"
        self.btnEditScript.flTitleColor = .white
        self.btnEditScript.flBackgroundColor = .gray
        self.btnEditScript.flCornerRadius = 4
        
        self.btnAddScript.bind(to: yoga, line1OfLeftBodySpan) { it in
            it.height = 26
            it.width = 80
            it.marginLeft = 12
        }
        self.btnAddScript.flTitle = "New Script"
        self.btnAddScript.flTitleColor = .white
        self.btnAddScript.flBackgroundColor = .gray
        self.btnAddScript.flCornerRadius = 4
        // line2 of leftBody
        let line2OfLeftBodySpan = NSView()
        line2OfLeftBodySpan.bind(to: yoga, leftBodySpan) { it in
            it.direction = .row
            it.height = 30
            it.alignItems = .center
        }
        self.btnCustomScript.bind(to: yoga, line2OfLeftBodySpan) { it in
            it.height = 26
            it.width = 80
            it.marginLeft = 4
        }
        self.btnCustomScript.flTitle = "DSL Script"
        self.btnCustomScript.flTitleColor = .white
        self.btnCustomScript.flBackgroundColor = .gray
        self.btnCustomScript.flCornerRadius = 4
        
        self.btnSwitchScript.bind(to: yoga, line2OfLeftBodySpan) { it in
            it.height = 26
            it.width = 80
            it.marginLeft = 12
        }
        self.btnSwitchScript.flTitle = "Load Script"
        self.btnSwitchScript.flTitleColor = .white
        self.btnSwitchScript.flBackgroundColor = .darkGray
        self.btnSwitchScript.flCornerRadius = 4
        // table
        if self.table == nil {
            let titles: [(String, Float, Float)] = [("pos", 0, 25),
                                                    ("bundle", 0, 80),
                                                    ("belong", 0, 75),
                                                    ("type", 0, 70),
                                                    ("invokeInfo", 388, 388),
                                                    ("remark", 0, 350)]
            self.table = buildColumnsTableView(yoga, columnTitles: titles)
            self.table.bind(to: yoga, self.bodyView) { it in
                it.flexGrow = 1
                it.height = 96
            }
            self.table.enableAutoShowLatestRows()
        }
        // right span
        let rightBodySpan = NSView()
        rightBodySpan.bind(to: yoga, self.bodyView) { it in
            it.marginRight = 4
            it.alignItems = .center
            it.marginLeft = 20
        }
        // line1 of rightBody
        let line1OfRightBodySpan = NSView()
        line1OfRightBodySpan.bind(to: yoga, rightBodySpan) { it in
            it.direction = .row
            it.alignItems = .center
            it.height = 30
            it.width = 220
        }
        self.selectInvokeMode.bind(to: yoga, line1OfRightBodySpan) { it in
            it.width = 136
            it.height = 30
        }
        
        self.btnInvokeScript.bind(to: yoga, line1OfRightBodySpan) { it in
            it.height = 26
            it.width = 80
            it.marginLeft = 4
        }
        self.btnInvokeScript.flTitleColor = .white
        self.btnInvokeScript.flBackgroundColor = .orange
        self.btnInvokeScript.flCornerRadius = 4
        // line2 of rightBody
        let line2OfRightBodySpan = NSView()
        line2OfRightBodySpan.bind(to: yoga, rightBodySpan) { it in
            it.direction = .row
            it.alignItems = .center
            it.height = 30
            it.width = 220
        }
        let lblRunModeTitle = FLLabel()
        lblRunModeTitle.bind(to: yoga, line2OfRightBodySpan) { it in
            it.marginLeft = 4
            it.height = 16
        }
        lblRunModeTitle.flText = "RunMode:"
        lblRunModeTitle.textColor = .gray
        lblRunModeTitle.font = NSFont.systemFont(ofSize: 14)
        
        self.lblRunMode.bind(to: yoga, line2OfRightBodySpan) { it in
            it.width = 62
            it.height = 16
        }
        self.lblRunMode.textColor = .darkGray
        self.lblRunMode.font = NSFont.boldSystemFont(ofSize: 14)
        
        self.btnNextStep.bind(to: yoga, line2OfRightBodySpan) { it in
            it.height = 26
            it.width = 80
            it.marginLeft = 4
        }
        self.btnNextStep.flTitle = "Step Next"
        self.btnNextStep.flTitleColor = .white
        self.btnNextStep.flBackgroundColor = .gray
        self.btnNextStep.flCornerRadius = 4
        // line3 of rightBody
        let line3OfRightBodySpan = NSView()
        line3OfRightBodySpan.bind(to: yoga, rightBodySpan) { it in
            it.direction = .rowReverse
            it.alignItems = .center
            it.height = 30
            it.width = 220
        }
        self.btnChangeToManual.bind(to: yoga, line3OfRightBodySpan) { it in
            it.height = 26
            it.width = 80
        }
        self.btnChangeToManual.flTitle = "Use Manual"
        self.btnChangeToManual.flTitleColor = .white
        self.btnChangeToManual.flBackgroundColor = .gray
        self.btnChangeToManual.flCornerRadius = 4
    }
}
